package com.pompom.matchcrawler;

import gummy.database.GummyClient;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;

import nl.ansuz.android.steam.dota.response.match.GetMatchDetailsResponse;
import nl.ansuz.android.steam.dota.vo.AdditionalUnitVO;
import nl.ansuz.android.steam.dota.vo.PickBanVO;
import nl.ansuz.android.steam.dota.vo.PlayerVO;

import com.pompom.matchcrawler.types.TOAccountWorkQueue;
import com.pompom.matchcrawler.types.TOInvalidMatch;
import com.pompom.matchcrawler.types.TOInventory;
import com.pompom.matchcrawler.types.TOMatchErrorQueue;
import com.pompom.matchcrawler.types.TOMatchStats;
import com.pompom.matchcrawler.types.TOMatchTeamInfo;
import com.pompom.matchcrawler.types.TOMatches;
import com.pompom.matchcrawler.types.TOPickbans;
import com.pompom.matchcrawler.types.TOPlayer;
import com.pompom.matchcrawler.types.TOPlayerWorkQueue;
import com.pompom.matchcrawler.types.TOSkillBuildNew;
import com.pompom.matchcrawler.types.TOUnitName;


public class MatchPersistor {

	private   static long processCount=0,lastTimer=0;
	/**
	 * 
	 * @param response
	 * @return true se ja existe match
	 */
	static LinkedHashSet<Long> persistedMatches = new LinkedHashSet<Long>(); 
	private static final int MAX_SET_SIZE = 48000;
	private MPBuffer mpbuffer = new MPBuffer();
	public   boolean persist(GetMatchDetailsResponse response )
	{
		return persist(response,false);
	}
	public   boolean persist(GetMatchDetailsResponse response, boolean immediate)
	{
		//todo clean hashset
		synchronized (persistedMatches)
		{
			if (!persistedMatches.add(response.matchId))
			{ 
				if (persistedMatches.size()>MAX_SET_SIZE)
				{

					Iterator<Long> i = persistedMatches.iterator();
					ArrayList <Long> toRemove = new ArrayList<Long>();
					for (int a=0;a<MAX_SET_SIZE/2;a++)
					{
						toRemove.add(i.next());
						
					}
					
					for (int k=toRemove.size()-1;k>=0;k--)
					{
						persistedMatches.remove (toRemove.get(k));
					}

				}
				return true;
			}
		}
		
		long timenow = System.currentTimeMillis();
		try {
			TOMatches match = new TOMatches();
			match.setMatchid(  response.matchId); 
			match.setMatchSeqnum(response.matchSequenceNumber);
			match.setGamemode((byte)response.gameMode);
			match.setFirstbloodTime(response.firstBloodTime);
			match.setLeagueId(response.leagueId);
			match.setLobbyType((byte)response.lobbyType);
			match.setHumanplayers((byte)response.humanPlayers);
			match.setClusterId(response.cluster);
			match.setSeason(response.season);
			match.setRadiantwin(response.radiantWin?(byte)1:0);
			match.setMatchDuration(response.duration);
			match.setStarttime(response.startTime);
			try{
				if (immediate)
				{
					GummyClient.getInstance("DT2").executeInsertTO(match);
				}
				else
				{
					if ( isMatchAlreadyInDB(response.matchId)==0)
					{
					mpbuffer.insertDelayed(match);
					}
					else
						return true;
				}
			} catch (RuntimeException e){

				if (e.getCause().getCause() instanceof com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException)
				{
					//TODO logar em tabela de erro ?
					System.out.println("Match already exists ! skipping "+response.matchId);
					updatePlayerId(response);
					return true;
				}
				else throw e;
			}
			//			System.out.println("step A"+(timenow-System.currentTimeMillis()));
			//			timenow = System.currentTimeMillis();
			/** Tower e Rax status **/

			TOMatchStats matchstat = new TOMatchStats();

			matchstat.setRaxDire(response.barracksStatusDire);
			matchstat.setRaxRadiant(response.barracksStatusRadiant);
			matchstat.setTowRadiant(response.towerStatusRadiant);
			matchstat.setTowDire(response.towerStatusDire); 
			matchstat.setMatchid(response.matchId);  

			if (immediate)
			{
				GummyClient.getInstance("DT2").executeInsertTO(matchstat);
			}
			else
			{
				mpbuffer.insertDelayed(matchstat);
			}
			//			System.out.println("step B"+(timenow-System.currentTimeMillis()));
			//			timenow = System.currentTimeMillis();

			/**Pickbans **/
			if (response.picksBans!=null)
			{

				for (int i=0;i<response.picksBans.length;i++)
				{
					PickBanVO pickban = response.picksBans[i];
					TOPickbans toPickban = new TOPickbans();
					toPickban.setMatchid(response.matchId);
					toPickban.setIspick(pickban.isPick?(byte)1:0);
					toPickban.setHeroId(pickban.heroId);
					toPickban.setTeam((byte)pickban.team);
					toPickban.setXorder((byte)pickban.order);
					if (immediate)
					{
						GummyClient.getInstance("DT2").executeInsertTO(toPickban);
					}
					{
						mpbuffer.insertDelayed(toPickban);
					}

				}
			}

			/*** Team Info ***/
			if (response.radiantTeamId!=0)
			{
				TOMatchTeamInfo toTeamInfoRadiant = new TOMatchTeamInfo();
				toTeamInfoRadiant.setMatchid(response.matchId);
				toTeamInfoRadiant.setSide((byte)0);//radiant
				toTeamInfoRadiant.setTeamid(response.radiantTeamId);
				toTeamInfoRadiant.setTeamLogo(response.radiantTeamLogo);
				toTeamInfoRadiant.setTeamName(response.radiantTeamName);
				toTeamInfoRadiant.setComplete((byte)response.radiantTeamComplete);
				if (immediate)
				{ 
					GummyClient.getInstance("DT2").executeInsertTO(toTeamInfoRadiant);									
				}
				else
				{
					mpbuffer.insertDelayed(toTeamInfoRadiant);
				}
			}


			if (response.direTeamId!=0)
			{
				TOMatchTeamInfo toTeamInfoDire = new TOMatchTeamInfo();
				toTeamInfoDire.setMatchid(response.matchId);
				toTeamInfoDire.setSide((byte)1);//dire
				toTeamInfoDire.setTeamid(response.direTeamId);
				toTeamInfoDire.setTeamLogo(response.direTeamLogo);
				toTeamInfoDire.setTeamName(response.direTeamName);
				toTeamInfoDire.setComplete((byte)response.direTeamComplete);
				if (immediate)
				{
					GummyClient.getInstance("DT2").executeInsertTO(toTeamInfoDire);
				}
				else
				{
					mpbuffer.insertDelayed(toTeamInfoDire);
				}
			}


			persistPlayers(response,immediate);

			persistSkillBuild(response,immediate); 
			checkMatchValidity(response,immediate); 

		}
		catch (Exception e)
		{
			e.printStackTrace();
			System.out.println("Error persisting match "+response.matchId);
			TOMatchErrorQueue errorMatch= new TOMatchErrorQueue();
			errorMatch.setMatchid(response.matchId);
			GummyClient.getInstance("QUEUES").executeInsertTO(errorMatch);
		}


		//synchronized(MatchPersistor.class)
		{
			processCount++;
			if (System.currentTimeMillis()-lastTimer>  10*60 * 1000 && response.matchId% 127 ==0)
			{
				long rpm = processCount/10;

				System.out.println("RPM:"+rpm);
				lastTimer=System.currentTimeMillis();
				processCount=0;

			}
		}

		return false;

	}

	private   void checkMatchValidity(GetMatchDetailsResponse response, boolean immediate)
	{

		boolean hasDead=false;
		for (int i=0;i<response.players.length;i++)
		{
			PlayerVO p =  response.players[i];

			if (p.deaths>0) hasDead=true;

			if (p.leaverStatus>1)
			{
				if (response.duration<5*60 || (response.duration < 6*60 &&!hasDead))
				{

					TOInvalidMatch im = new TOInvalidMatch();
					im.setMatchid(response.matchId);

					if (immediate)
					{
						GummyClient.getInstance("DT2").executeInsertTO(im);
					}
					{
						mpbuffer.insertDelayed(im);
					}

					System.out.println("Match recorded as invalid "+response.matchId);
					break;

				}

			}
		}
	}

	private   void updatePlayerId(GetMatchDetailsResponse response)
	{
		for (int i=0;i<response.players.length;i++)
		{
			PlayerVO p =  response.players[i];

			if (p.accountId>0)
				GummyClient.getInstance("DT2").execute("UPDATE PLAYER SET ACCOUNTID = "+p.accountId+" WHERE MATCHID = "+response.matchId+ 
						" AND PLAYERSLOT ="+p.playerSlot, new Object[]{  });

		}
	}

	private   void persistPlayers(GetMatchDetailsResponse response, boolean immediate)
	{
		LinkedList<Long> accountIdsToUpdate = new LinkedList<Long>();

		for (int i=0;i<response.players.length;i++)
		{
			PlayerVO p =  response.players[i];
			TOPlayer toplayer = new TOPlayer();

			byte side =(byte) (p.playerSlot>=128?1:0);//1 = dire

			if (p.accountId>0)
				accountIdsToUpdate.add((long)p.accountId);
			else
				p.accountId=0; //setar 0 como desconhecido pra caber no uint32

			toplayer.setMatchid(response.matchId);
			toplayer.setAccountid((long)p.accountId);
			toplayer.setPlayerslot(p.playerSlot); 
			toplayer.setHeroId(p.heroId);
			toplayer.setKills(p.kills);
			toplayer.setDeaths(p.deaths);
			toplayer.setAssists(p.assists);
			toplayer.setLeaverstatus((byte)p.leaverStatus);
			toplayer.setGold(p.gold);
			toplayer.setLasthits(p.lastHits);
			toplayer.setDenies(p.denies);
			toplayer.setGpm(p.goldPerMinute);
			toplayer.setXpm(p.xpPerMinute);
			toplayer.setGoldspent(p.goldSpent);
			toplayer.setHerodamage(p.heroDamage);
			toplayer.setTowerdamage(p.towerDamage);
			toplayer.setHeal(p.heroHealing);
			toplayer.setXlevel((byte)p.level);
			if (p.additionalUnits!=null)
			{
				for (int k=0;k<p.additionalUnits.length;k++)
				{ 
					AdditionalUnitVO auvo = p.additionalUnits[k];

					persistInventoryItems(response.matchId,256+k, new int[]{auvo.item0,auvo.item1,auvo.item2,auvo.item3,auvo.item4,auvo.item5},immediate);
					TOUnitName unitName= new TOUnitName();
					unitName.setMatchid(response.matchId);
					unitName.setHeroId(256+k);
					unitName.setUnitName(auvo.unitName);

					if (immediate)
					{
						GummyClient.getInstance("DT2").executeInsertTO(unitName);
					}
					else
					{
						mpbuffer.insertDelayed(unitName);
					}
				}

			}


			if (immediate)
			{
				GummyClient.getInstance("DT2").executeInsertTO(toplayer);
			}
			else
			{
				mpbuffer.insertDelayed(toplayer);
			}


			/**Items**/
			persistInventoryItems(response.matchId,p.heroId, new int[]{p.item0,p.item1,p.item2,p.item3,p.item4,p.item5},immediate);





		}

		putAccountsInQueue(accountIdsToUpdate);
		putPlayersInQueue(accountIdsToUpdate);
	}

	public   void persistSkillBuild(GetMatchDetailsResponse response, boolean immediate)
	{
		for (int i=0;i<response.players.length;i++)
		{
			PlayerVO p =  response.players[i];
			TOPlayer toplayer = new TOPlayer();

			if (p.abilityUpgrades!=null)
			{
				int abilityVector[] = new int[p.abilityUpgrades.length];
				for (int j=0;j<p.abilityUpgrades.length;j++)
				{
					abilityVector[j]= p.abilityUpgrades[j].ability;

				}

				long skillbuild = SkillMapBuilder.getBuildFromArray(abilityVector, p.heroId, response.matchId);
				TOSkillBuildNew b = new TOSkillBuildNew();
				b.setMatchid(response.matchId);
				b.setHeroId(p.heroId);
				b.setBuild(skillbuild);

				if (immediate)
				{
					GummyClient.getInstance("DT2").executeInsertTO(b);
				}
				else
				{
					mpbuffer.insertDelayed(b);
				}


			}
		}
	}


	private static void putPlayersInQueue(LinkedList<Long> accountIdsToUpdate)
	{
		if (!accountIdsToUpdate.isEmpty())
			for (int i=0;i<accountIdsToUpdate.size();i++)
			{

				TOPlayerWorkQueue toAcc = new TOPlayerWorkQueue();
				toAcc.setAccountId(accountIdsToUpdate.get(i));

				try{
					GummyClient.getInstance("QUEUES").executeInsertTO(toAcc);
					//System.out.println("inserted "+toAcc.getAccountId()+" into PlayerWorkQueue");
				}
				catch (RuntimeException e){

					if (e.getCause().getCause() instanceof com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException)
					{

					}
					else
						throw e;
				}

			}
	}	
	private static void putAccountsInQueue(LinkedList<Long> accountIdsToUpdate)
	{
		if (!accountIdsToUpdate.isEmpty())
			for (int i=0;i<accountIdsToUpdate.size();i++)
			{

				TOAccountWorkQueue toAcc = new TOAccountWorkQueue();
				toAcc.setAccountId(accountIdsToUpdate.get(i));

				try{
					GummyClient.getInstance("QUEUES").executeInsertTO(toAcc);
					//System.out.println("inserted "+toAcc.getAccountId()+" into account workQueue");
				}
				catch (RuntimeException e){

					if (e.getCause().getCause() instanceof com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException)
					{

					}
					else
						throw e;
				}

			}
	}

	private   final void persistInventoryItems(long matchid,int heroid, int items[], boolean immediate)
	{


		/**Items**/
		for (int j=0;j<6;j++)
		{
			int NO_ITEM=0; 



			if (items[j]!=NO_ITEM)
			{
				TOInventory inventoryItem = new TOInventory();
				inventoryItem.setMatchid(matchid);
				inventoryItem.setHeroId(heroid); 
				inventoryItem.setItemId(items[j]);

				if (immediate)
				{
					GummyClient.getInstance("DT2").executeInsertTO(inventoryItem);
				}
				else
				{
					mpbuffer.insertDelayed(inventoryItem);
				}
			}


		}

	}


	/**
	 * 
	 * @param matchid
	 * @return  0 = match nao ta no bd
	 * 			1 = match e skillbuild in db
	 * 	        2 = match sim, skilbuild ainda nao
	 */
	public static int isMatchAlreadyInDB(long matchid)
	{

		boolean matchInDb =  GummyClient.getInstance("DT2").executeQueryBoolean("SELECT 1 FROM MATCHES WHERE MATCHID =?", new Object[]{  (long)matchid});

		if (!matchInDb)
			return 0;



		return 1;





	}

	public void setBufferSize(int bsize)
	{
		mpbuffer.BUFFER_SIZE=bsize;

	}

	public void flushAll()
	{
		mpbuffer.flushAll();
	}
}
